﻿using System;
using System.Collections.Generic;
using System.Text;

namespace ArithExpInterp
{
	public class Program
	{
		static void Main(string[] args)
		{
			Var[] env = new Var[3];
			env[0] = new Var("x");
			env[1] = new Var("y");

			Sub subvv = new Sub(env[1], env[0]);
			Add addvc = new Add(env[1], new Const(5.0f));
			Mult multas = new Mult(addvc, subvv);
			Add addtl = new Add(multas, new Const(3.0f));

			Dictionary<Var, float> binds = new Dictionary<Var, float>();
			binds.Add(env[0], 2.0f);
			binds.Add(env[1], 7.0f);

			float result = addtl.Eval(binds);

			Console.WriteLine("Expression=" + addtl.ToString());
			Console.WriteLine(String.Format("Bindings=[{0}->{1}, {2}->{3}]", env[0].Name, binds[env[0]], env[1].Name, binds[env[1]]));
			Console.WriteLine("Result=" + result);
		}
	}

	public abstract class ArithExp
	{
 		public abstract float Eval(Dictionary<Var, float> vbind);
	}

	public sealed class Var : ArithExp
	{
		public readonly string Name;

		public Var(string name)
			: base()
		{ this.Name = name; }

		public override string ToString()
		{ return this.Name; }

		public override float Eval(Dictionary<Var, float> vbind)
		{ return vbind[this]; }
	}

	public sealed class Const : ArithExp
	{
		public readonly float Value;

		public Const(float value)
			: base()
		{ this.Value = value; }

		public override string ToString()
		{ return this.Value.ToString(); }

		public override float Eval(Dictionary<Var, float> vbind)
		{ return this.Value; }
	}

	public abstract class BinExp : ArithExp
	{
		public readonly ArithExp LExp;
		public readonly ArithExp RExp;

		public BinExp(ArithExp l, ArithExp r)
			: base()
		{
			this.LExp = l;
			this.RExp = r;
		}
	}

	public sealed class Add : BinExp
	{
		public Add(ArithExp l, ArithExp r)
			: base(l, r)
		{ ;}

		public override string ToString()
		{ return String.Format("({0} + {1})", this.LExp.ToString(), this.RExp.ToString()); }

		public override float Eval(Dictionary<Var, float> vbind)
		{ return this.LExp.Eval(vbind) + this.RExp.Eval(vbind); }
	}

	public sealed class Sub : BinExp
	{
		public Sub(ArithExp l, ArithExp r)
			: base(l, r)
		{ ;}

		public override string ToString()
		{ return String.Format("({0} - {1})", this.LExp.ToString(), this.RExp.ToString()); }

		public override float Eval(Dictionary<Var, float> vbind)
		{ return this.LExp.Eval(vbind) - this.RExp.Eval(vbind); }
	}

	public sealed class Mult : BinExp
	{
		public Mult(ArithExp l, ArithExp r)
			: base(l, r)
		{ ;}

		public override string ToString()
		{ return String.Format("({0} * {1})", this.LExp.ToString(), this.RExp.ToString()); }

		public override float Eval(Dictionary<Var, float> vbind)
		{ return this.LExp.Eval(vbind) * this.RExp.Eval(vbind); }
	}

	public sealed class Div : BinExp
	{
		public Div(ArithExp l, ArithExp r)
			: base(l, r)
		{ ;}

		public override string ToString()
		{ return String.Format("({0} / {1})", this.LExp.ToString(), this.RExp.ToString()); }

		public override float Eval(Dictionary<Var, float> vbind)
		{ return this.LExp.Eval(vbind) / this.RExp.Eval(vbind); }
	}
}
